#include "pi_timer_service.hpp"
#include <list>
#include <mutex>
#include "core/assert.hpp"
#include "core/log.hpp"
#include "job/js.hpp"
#include "service/timer_service.hpp"

class Task;

#define MAX_TASK_CNT 2048

static Task      *tasks[MAX_TASK_CNT];
static u32        task_count = 0;
static std::mutex task_mtx;

#define LOCK_TASK_LIST std::lock_guard lck(task_mtx)

enum task_state
{
    idle,
    pending,
    running,

    rm, // to be removed
};

class Task final : public ITask, public std::enable_shared_from_this<Task>
{
    pi_timer_service       *service_;
    ITimerService::proc     proc_;
    u64                     cycle_ms_;
    u64                     delay_ms_{};
    f64                     next_timeout_{};
    u32                     count_;
    u32                     idx{};
    bool                    auto_rm;
    std::atomic<task_state> state;

    friend class pi_timer_service;

public:
    Task (pi_timer_service *svc, ITimerService::proc proc, f64 creation_time) : service_(svc), proc_ (std::move (proc)), cycle_ms_ (0), count_ (0), auto_rm{true}, state {idle}
    {
        // L_INFO("task %p create", this);
    }

    ~Task () override
    {
        // L_INFO("task %p remove", this);
    }

    ITask *cycle (u64 ms) override
    {
        cycle_ms_ = ms;
        return this;
    }

    ITask *delay (u64 ms) override
    {
        delay_ms_ = ms;
        return this;
    }

    void start () override
    {
        start_repeat (1);
    }

    void start_repeat (u32 times) override
    {
        next_timeout_ = service_->get_time() + delay_ms_ * 0.001;
        count_ = times;
        state  = pending;
    }

    void stop () override
    {
        state = idle;
    }

    f64 timeout () const override
    {
        return next_timeout_;
    }

    void execute () override
    {
        if (proc_)
            proc_ ();
    }

    bool _execute ()
    {
        execute ();
        if (count_ == ~0u)
            return true;
        if (count_ > 0) {
            if (--count_ > 0)
                return true;
            else if (auto_rm) {
                state = rm;
                cycle_ms_ = 0;
                proc_ = [this] {
                    remove();
                };
                return true;
            }
        }
        return false;
    }

    void set_id (u32 id)
    {
        idx = id;
    }

    void remove() override
    {
        LOCK_TASK_LIST;

        std::swap(tasks[idx], tasks[--task_count]);
        tasks[task_count] = nullptr;

        if (state == idle) {
            delete this;
        } else {
            state = rm;
        }
    }

    ITask* auto_remove(bool on) override
    {
        auto_rm = on;
        return this;
    }

};

void pi_timer_service::on_tick (pi_time_t delta, pi_time_t time)
{
    system_time += f64 (delta);

    LOCK_TASK_LIST;

    for (u32 i = 0; i < task_count; ++i)
    {
        if (tasks[i] && tasks[i]->state == pending)
        {
            if (tasks[i]->next_timeout_ <= system_time)
            {
                tasks[i]->state = running;
                submit_job (create_job ([t = tasks[i], this] {
                    if (t->_execute ())
                    {
                        t->next_timeout_ = get_time() + t->cycle_ms_ * 0.001;
                        task_state st = running;
                        if (!t->state.compare_exchange_strong (st, pending)) // 如果是running状态则改为pending状态
                        {
                            t->state = idle;
                            t->remove();
                        }
                    }
                    else
                    {
                        if (t->state == rm) {

                        } else {
                            t->state = idle;
                        }
                    }
                }));
            }
        }
    }
}

ITask *pi_timer_service::create_scheduler_impl (proc proc)
{
    LOCK_TASK_LIST;

    PI_ASSERT (proc != nullptr);

    Task *task = new Task (this, proc, system_time);
    task->set_id (task_count);
    tasks[task_count++] = task;

    return task;
}
